<?php
declare(strict_types=1);

namespace Plugin\Alarm\Service;

use App\Exception\BusinessException;
use App\Http\Common\ResultCode;
use App\Repository\Permission\UserRepository;
use App\Service\IService;
use Common\Common;
use Hyperf\DbConnection\Db;
use Plugin\Alarm\Event\WorkAcceptEvent;
use Plugin\Alarm\Event\WorkCloseEvent;
use Plugin\Alarm\Event\WorkCreatedEvent;
use Plugin\Alarm\Event\WorkReviewEvent;
use Plugin\Alarm\Event\WorkTransferEvent;
use Plugin\Alarm\Model\AlarmWorkStatus;
use Plugin\Alarm\Process\ReviewConsumerProcess;
use Plugin\Alarm\Repository\RdAlarmDangerRepository;
use Plugin\Alarm\Repository\RdAlarmHandleRecordRepository;
use Plugin\Alarm\Repository\RdAlarmWarnRecordRepository;
use Plugin\Alarm\Repository\RdAlarmWorkApplyRepository;
use Plugin\Alarm\Repository\RdAlarmWorkRepository;
use Plugin\Organization\Repository\RdOrganizeUserStandRepository;

final class RdAlarmWorkService extends IService
{
    public function __construct(
        protected readonly RdAlarmWorkRepository         $repository,
        protected readonly RdAlarmHandleRecordRepository $handleRecordRepository,
        protected readonly RdAlarmWorkApplyRepository    $applyRepository,
        protected readonly RdAlarmWarnRecordRepository   $warnRecordRepository,
        protected readonly RdAlarmDangerRepository       $dangerRepository,
        protected readonly UserRepository                $userRepository
    )
    {
    }

    public function apiPage(array $params, int $page = 1, int $pageSize = 10): array
    {
        try {
            $adminStands = Common::container()->get(RdOrganizeUserStandRepository::class)->getAdminStands($params['user_id']);
        } catch (\Throwable $e) {
            Common::logException(__CLASS__, $e);
            $adminStands = [];
        }

        return $this->repository->apiPage($params, $page, $pageSize, $adminStands);
    }

    public function workCreate(array $data)
    {
        $standId = $data['stand_id'];
        $userId = $this->getFreeSafetyOfficer($standId);
        if (!$userId) {
            return false;
        }

        $work = $this->repository->create(array_merge($data, [
            'user_id' => $userId,
        ]));
        $this->handleRecordRepository->createHandle($work->id, $data['created_by']);
        $this->getEventDispatcher()->dispatch(new WorkCreatedEvent($work->toArray(), $work->stand_id, $userId));
        return $work;
    }

    public function getFreeSafetyOfficer($standId)
    {
        try {
            $users = Common::container()->get(RdOrganizeUserStandRepository::class)->getStandUsers($standId, 2);
        } catch (\Throwable $e) {
            Common::logException(__CLASS__, $e);
            return 0;
        }
        if (empty($users)) {
            return 0;
        }

        $userId = 0;
        $WorkCount = PHP_INT_MAX;
        foreach ($users as $user) {
            $workCount = $this->repository->getQuery()
                ->where('user_id', $user)
                ->where('status', '<', AlarmWorkStatus::Review->value)
                ->count();

            if ($workCount < $WorkCount) {
                $userId = $user;
                $WorkCount = $workCount;
            }
        }

        return $userId;
    }

    public function read(int $id, array $data): array
    {
        $work = $this->checkPermission($id, $data);
        $this->handleRecordRepository->readHandle($id, $data['updated_by']);
        if ($work['status'] == AlarmWorkStatus::Pending->value) {
            if ($this->updateById($id, ['status' => AlarmWorkStatus::Handle->value])) {
                $work['status'] = AlarmWorkStatus::Handle->value;
            }
        }
        $handle = $this->handleRecordRepository->getQuery()
            ->where('work_id', $id)->with('user:id,username')
            ->get()->toArray();
        $work['handle'] = $handle;
        $work['username'] = $this->userRepository->getQuery()->whereKey($work['user_id'])->pluck('username')->first();
        return $work;
    }

    public function checkPermission(int $id, array $data): array
    {
        $work = $this->repository->findById($id);
        if (!$work) {
            throw new BusinessException(ResultCode::NOT_FOUND, '未找到该工单');
        }
        try {
            $userStandRepository = Common::container()->get(RdOrganizeUserStandRepository::class);
        } catch (\Throwable $e) {
            Common::logException(__CLASS__, $e);
            throw new BusinessException(ResultCode::NOT_FOUND, '获取用户组织架构信息失败');
        }
        $role = $userStandRepository->getStandRole($work->stand_id, $data['updated_by']);
        if ($role != 1 && $work->user_id != $data['updated_by']) {
            throw new BusinessException(ResultCode::FORBIDDEN, '您没有权限处理该工单');
        }
        $work = $work->toArray();
        $work['related'] = $this->getRelatedData($work);
        return $work;
    }

    public function getRelatedData($work): array
    {
        switch ($work['type']) {
            case 1: //告警
                return $this->warnRecordRepository->getQuery()->whereKey($work['related_id'])->with(['msg', 'msg.video', 'addr:id,addr'])->get()->toArray();
            case 2: //隐患
                return $this->dangerRepository->getQuery()->whereKey($work['related_id'])->get()->toArray();
            case 3: //其他
                return [];
            default:
                return [];
        }
    }

    public function transfer(int $id, array $params)
    {
        $work = $this->checkPermission($id, $params);
        if ($work['status'] >= AlarmWorkStatus::Review->value) {
            throw new BusinessException(ResultCode::FORBIDDEN, '该工单正在等待AI复核或已处理完成，无法转派。');
        }
        $toUser = intval($params['to_user']);
        $userStandRepository = Common::container()->get(RdOrganizeUserStandRepository::class);
        $role = $userStandRepository->getStandRole($work['stand_id'], $toUser);
        if ($role == 0) {
            throw new BusinessException(ResultCode::FORBIDDEN, '用户不属于该站点');
        }
        if ($this->applyRepository->findByFilter(['work_id' => $id, 'status' => 1])) {
            throw new BusinessException(ResultCode::FORBIDDEN, '该工单有未处理的转派申请');
        }
        $apply = $this->applyRepository->create([
            'work_id' => $id,
            'created_by' => $work['user_id'],
            'user_id' => $toUser,
            'remark' => $params['remark'] ?? '',
        ]);
        if ($apply) {
            $this->handleRecordRepository->transferHandle($id, $params['updated_by'], $params);
            $this->getEventDispatcher()->dispatch(new WorkTransferEvent($work, $work['stand_id'], $toUser));
            return $apply;
        }
        throw new BusinessException(ResultCode::NOT_FOUND, '转派申请失败');
    }

    public function transfer2(int $id, array $params)
    {
        $work = $this->checkPermission($id, $params);
        if ($work['status'] >= AlarmWorkStatus::Review->value) {
            throw new BusinessException(ResultCode::FORBIDDEN, '该工单正在等待AI复核或已处理完成，无法转派。');
        }
        $toUser = intval($params['to_user']);
        if ($work['user_id'] == $toUser) {
            throw new BusinessException(ResultCode::FORBIDDEN, '该用户已是该工单处理人，请勿重复转派。');
    }
        $userStandRepository = Common::container()->get(RdOrganizeUserStandRepository::class);
        $role = $userStandRepository->getStandRole($work['stand_id'], $toUser);
        if ($role == 0) {
            throw new BusinessException(ResultCode::FORBIDDEN, '用户不属于该站点');
        }
        Db::beginTransaction();
        try {
            $this->handleRecordRepository->transferHandle($id, $params['updated_by'], $params);
            $this->updateById($id, ['user_id' => $toUser]);
            $this->getEventDispatcher()->dispatch(new WorkTransferEvent($work, $work['stand_id'], $toUser));
            Db::commit();
        } catch (\Throwable $e) {
            Db::rollBack();
            Common::logException(__CLASS__, $e);
            throw new BusinessException(ResultCode::NOT_FOUND, '转派申请失败');
        }
        return true;
    }

    public function accept(int $id, array $params)
    {
        $userId = $params['updated_by'];
        $apply = $this->applyRepository->findByFilter(['work_id' => $id, 'user_id' => $userId]);
        if (!$apply) {
            throw new BusinessException(ResultCode::NOT_FOUND, '未找到申请记录');
        }
        if ($apply->status != 1) {
            throw new BusinessException(ResultCode::FORBIDDEN, '申请已处理');
        }
        Db::beginTransaction();
        try {
            $apply->update(['status' => $params['status'] == 2 ? 2 : 3, 'handle_remark' => $params['remark'] ?? '',]);
            $work = $this->repository->findById($id);
            $fromUser = $work->user_id;
            $work->update(['user_id' => $userId]);
            $this->handleRecordRepository->acceptTransferHandle($id, $userId);
            Db::commit();
        } catch (\Throwable $e) {
            Db::rollBack();
            Common::logException(__CLASS__, $e);
            throw new BusinessException(ResultCode::NOT_FOUND, '接受转派申请失败');
        }
        $this->getEventDispatcher()->dispatch(new WorkAcceptEvent($work->toArray(), $work->stand_id, $fromUser, $params['status'], $params['remark'] ?? ''));
        return true;
    }

    public function handle(int $id, array $params)
    {
        $work = $this->checkPermission($id, $params);
        $result = $params['result'];
        Db::beginTransaction();
        try {
            $this->handleRecordRepository->apiCreate($id, $params['updated_by'], $params['handle_type'] ?? 0, $params['handle_info']);
            if ($result == 1) {
                $this->updateById($id, ['status' => AlarmWorkStatus::Review->value]);
                Common::redisStreamAdd(ReviewConsumerProcess::STREAM_NAME, $work);
//                $this->getEventDispatcher()->dispatch(new WorkHandleFinishEvent($work));
            }
            Db::commit();
        } catch (\Throwable $e) {
            Db::rollBack();
            Common::logException(__CLASS__, $e);
            throw new BusinessException(ResultCode::NOT_FOUND, '处理工单失败');
        }

    }

    public function countTotal($userId): array
    {
        try {
            $adminStands = Common::container()->get(RdOrganizeUserStandRepository::class)->getAdminStands($userId);
        } catch (\Throwable $e) {
            Common::logException(__CLASS__, $e);
            $adminStands = [];
        }
        $work = $this->repository->getQuery()
            ->where('user_id', $userId)
            ->orWhereIn('stand_id', $adminStands)
            ->get()->groupBy('status')->toArray();
        return array_map(function ($item) {
            return count($item);
        }, $work);
    }

    public function close(int $id, array $array)
    {
        $work = $this->checkPermission($id, $array);
        Db::beginTransaction();
        try {
            $this->updateById($id, ['status' => AlarmWorkStatus::Close->value]);
            $this->handleRecordRepository->closeHandle($id, $array['updated_by'], ['remark' => $array['remark'] ?? '']);
            $this->getEventDispatcher()->dispatch(new WorkCloseEvent($work, $array['updated_by']));
            Db::commit();
        } catch (\Throwable $e) {
            Db::rollBack();
            Common::logException(__CLASS__, $e);
            throw new BusinessException(ResultCode::FAIL, '关闭工单失败');
        }
        return true;
    }

    /**
     * AI复核结果
     * @param int $id
     * @param array $array
     * @return void
     */
    public function review(int $id, array $array)
    {
        $work = $this->findById($id);
        if (!$work) {
            throw new BusinessException(ResultCode::NOT_FOUND, '工单不存在');
        }
        if ($work->status != AlarmWorkStatus::Review->value) {
            throw new BusinessException(ResultCode::FORBIDDEN, '工单状态异常');
        }
        Db::beginTransaction();
        try {
            $this->updateById($id, ['status' => $array['result'] == 1 ? AlarmWorkStatus::Handle->value : AlarmWorkStatus::Complete->value]);
            $this->handleRecordRepository->reviewHandle($id, $array['updated_by'] ?? 0, $array);
            $this->getEventDispatcher()->dispatch(new WorkReviewEvent($work, $array));
            // 确认消息
            if ($work['extra']['message_id'] ?? 0) {

                Common::redisStreamDeleteMessages(ReviewConsumerProcess::STREAM_NAME, [$work['extra']['message_id']]);
            }
            Db::commit();
        } catch (\Throwable $e) {
            Db::rollBack();
            Common::logException(__CLASS__, $e);
            throw new BusinessException(ResultCode::FAIL, 'AI复核失败');
        }
    }
}